home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The Fatted Calf
/
The Fatted Calf.iso
/
Applications
/
Publishing
/
ImagePortfolio
/
Source
/
GifImageRep.m
< prev
next >
Wrap
Text File
|
1994-04-01
|
12KB
|
453 lines
// -------------------------------------------------------------------------------------
// GifImageRep.m
// Martin D. Flynn, NeXT Computer, Inc.
// You may freely copy, distribute and reuse the code in this example.
// NeXT disclaims any warranty of any kind, expressed or implied, as to its
// fitness for any particular use.
//
// Note: Much of the source in this object has been a heavy modification of various
// public domain sources for GIF file conversion programs.
// -------------------------------------------------------------------------------------
#import <stdlib.h>
#import <stdio.h>
#import <string.h>
#import <libc.h>
#import <objc/objc.h>
#import <dpsclient/dpsclient.h>
#import <defaults/defaults.h>
#import <objc/List.h>
#import <appkit/tiff.h>
#import <appkit/NXImage.h>
#import <appkit/Application.h>
#import <appkit/nextstd.h>
#import "GifImageRep.h"
// -------------------------------------------------------------------------------------
// error codes & descriptions
#define errNONE 0
#define errTABLE_OVERFLOW 1
#define errRASTER_SIZE 2
#define errNO_COLORMAP 3
#define errINVALID_FILE 4
#define errBAD_BLOCK 5
#define errCANT_LOAD 6
#define errEND_OF_FILE 7
#define errNO_IMAGE 8
static char *errDesc[] = {
"none",
"code table overflow",
"raster has the wrong size",
"no colormap present for image",
"invalid GIF file",
"illegal GIF block type",
"unable to load file (unknown reason)",
"reached premature end-of-file"
"no image found in file",
0
};
const char *errorDesc(int x) { return errDesc[x]; }
// -------------------------------------------------------------------------------------
@implementation GifImageRep
// -------------------------------------------------------------------------------------
// internal support methods
// -------------------------------------------------------------------------------------
/* decode a raster image */
// Note: A 'goto'? Yuck! I know their ugly, but I just haven't remove it yet. sorry.
- (int)_readRaster
{
u_char *ch, buf[260], *fill;
u_int count, datm;
int i, err, code, bits, thisCode;
int codeTable[4096][2]; // LZW compression code data
int stack[4096 * 2], *sp;
int _datasize, _codesize, _maxsize;
int _clear, _avail, _oldcode, _firstcode;
/* initialize */
err = errNONE;
fill = imageRaster;
_datasize = getc(imageStream);
_clear = 1 << _datasize;
_codesize = _datasize + 1;
_avail = _clear + 2;
_maxsize = _clear * 2;
_oldcode = -1;
_firstcode = -1;
bits = 0;
datm = 0;
sp = stack;
/* prefill table */
for (i = 0; i < 4096; i++) {
codeTable[i][0] = 0;
codeTable[i][1] = (i < _clear)? i : 0;
}
/* decode raster */
for (count = getc(imageStream); count > 0; count = getc(imageStream)) {
/* read block */
if (fread(buf, 1, count, imageStream) != count) { err = errEND_OF_FILE; break; }
/* process block */
for (ch = buf; count-- > 0; ch++) {
datm += (u_int)(*ch) << bits;
bits += 8;
while (bits >= _codesize) {
/* get code */
code = datm & ((1 << _codesize) - 1);
datm >>= _codesize;
bits -= _codesize;
/* clear table */
if (code == _clear) {
_codesize = _datasize + 1;
_maxsize = _clear * 2;
_avail = _clear + 2;
_oldcode = -1;
_firstcode = -1;
sp = stack;
for (i = 0; i < 4096; i++) {
codeTable[i][0] = 0;
codeTable[i][1] = (i < _clear)? i : 0;
}
continue;
}
/* end of decompression */
if (code == _clear + 1) goto exitloop; /* for non-standard GIF files */
/* save first code */
if (_firstcode == -1) {
_firstcode = _oldcode = code;
*fill++ = code;
continue;
}
/* save this code */
thisCode = code;
/* code check */
if (code >= _avail) {
*sp++ = _firstcode;
code = _oldcode;
}
/* place codes on stack */
while (code >= _clear) {
*sp++ = codeTable[code][1];
if (code == codeTable[code][0]) {
err = errTABLE_OVERFLOW;
goto exitloop;
}
code = codeTable[code][0];
}
*sp++ = _firstcode = codeTable[code][1];
/* save in table */
code = _avail;
if (code < 4096) {
codeTable[code][0] = _oldcode;
codeTable[code][1] = _firstcode;
_avail++;
if ((_avail >= _maxsize) && (_maxsize < 4096)) {
_maxsize *= 2;
_codesize++;
}
}
/* save old code */
_oldcode = thisCode;
/* flush stack */
while (sp > stack) *fill++ = *--sp;
}
}
}
exitloop:
if (err == errEND_OF_FILE) {
err = errNONE;
NXLogError("GIF premature end of file");
}
if (!err && (fill != imageRaster + rasterSize)) err = errRASTER_SIZE;
return err;
}
/* load image raster */
- (int)_loadImage
{
u_char buf[10];
BOOL local, interleaved;
int err, left;
/* set file position */
if (imagePos) fseek(imageStream, imagePos, SEEK_SET);
else imagePos = ftell(imageStream);
/* read image information */
fread(buf, 1, 9, imageStream);
left = buf[0] + (buf[1] << 8);
rasterTop = buf[2] + (buf[3] << 8);
pixWide = buf[4] + (buf[5] << 8);
pixHigh = buf[6] + (buf[7] << 8);
local = (buf[8] & 0x80)? YES : NO;
interleaved = (buf[8] & 0x40)? YES : NO;
rasterSize = (u_long)pixWide * (u_long)pixHigh;
/* convert color map */
if (local) {
colorMapCount = 1 << ((buf[8] & 0x7) + 1);
fread(colorMap, 3, colorMapCount, imageStream);
} else
if (!isGlobalMap) return errNO_COLORMAP;
/* read image raster */
if (!imageRaster) memset((imageRaster=(u_char*)malloc(rasterSize)), 0, rasterSize);
if ((err = [self _readRaster]) && (err != errRASTER_SIZE)) return err;
/* remove any interleave */
if (interleaved) {
int i, r = 0;
u_char *temp = (u_char*)malloc(rasterSize);
u_short *tbl = (u_short*)malloc(pixHigh * sizeof(u_short));
for (i = rasterTop ; i < rasterTop + pixHigh; i += 8) tbl[i] = r++;
for (i = rasterTop + 4; i < rasterTop + pixHigh; i += 8) tbl[i] = r++;
for (i = rasterTop + 2; i < rasterTop + pixHigh; i += 4) tbl[i] = r++;
for (i = rasterTop + 1; i < rasterTop + pixHigh; i += 2) tbl[i] = r++;
for (i = 0, r = rasterTop; r < rasterTop + pixHigh; i++, r++)
memcpy(&temp[i * pixWide], &imageRaster[tbl[r] * pixWide], pixWide);
free(tbl);
free(imageRaster);
imageRaster = temp;
rasterTop = 0;
}
/* return successful */
return errNONE;
}
/* read GIF header info */
- (int)_readGIFHeader
{
u_int screenWidth, screenHeight; // screen dimensions
u_int background;
u_char buf[256];
/* rewind file pointer */
rewind(imageStream);
/* check gif signiture type */
if (![[self class] validImageType:imageStream]) return errINVALID_FILE;
/* read global information */
fread(buf, 1, 7, imageStream);
screenWidth = buf[0] + (buf[1] << 8); // not used
screenHeight = buf[2] + (buf[3] << 8); // not used
isGlobalMap = (buf[4] & 0x80)? YES : NO; // global color map?
background = buf[5]; // not used
aspectRatio = (buf[6])? ((float)buf[6] + 15.0) / 64.0 : 1.0; // aspect ratio
/* load global colormap */
if (isGlobalMap) {
colorMapCount = 1 << ((buf[4] & 0x07) + 1);
fread(colorMap, 3, colorMapCount, imageStream);
}
return errNONE;
}
/* initialize from file */
- (int)_readImageAtPosition:(long int)overridePos
{
int err;
/* read gif header info */
if (err = [self _readGIFHeader]) return err;
/* set file position override */
if (overridePos) fseek(imageStream, overridePos, SEEK_SET);
/* loop through file */
for(;;) {
int err;
u_char buf[256], ch = getc(imageStream);
switch (ch) {
case ',' :
if (err = [self _loadImage]) return err;
imageNextPos = ftell(imageStream);
return errNONE;
case '!' : // skip extension
for (ch=getc(imageStream); ch=getc(imageStream);)
fread(buf, 1, ch, imageStream);
break;
case '\0':
break; /* non-standard files */
case ';' :
return imageNextPos? errNONE: errNO_IMAGE;
default :
return errBAD_BLOCK;
}
}
/* return any error */
return errNONE;
}
// -------------------------------------------------------------------------------------
// internal init method
// -------------------------------------------------------------------------------------
/* init from file stream */
- _initFromFile:(const char*)filename atPos:(long int)overridePos
{
NXSize pSize;
/* generic initialization */
[self initDrawMethod:@selector(_drawImage:) inObject:self];
/* local var init */
imageRaster = (u_char*)nil;
pixWide = 0;
pixHigh = 0;
rasterSize = 0L;
isGlobalMap = NO;
colorMapCount = 0;
imagePos = 0L;
imageNextPos = 0L;
/* verify stream */
imageStream = fopen(filename, "r");
if (!imageStream) { [self free]; return (id)nil; }
/* initialize */
loadError = [self _readImageAtPosition:overridePos];
if (loadError == errNO_IMAGE) { [self free]; return (id)nil; }
if (!loadError && (!pixWide || !pixHigh)) loadError = errCANT_LOAD;
if (loadError) {
NXLogError("GIF stream error: %s", errDesc[loadError]);
[self free];
return (id)nil;
}
/* init size (check aspect ratio) */
pSize.width = (float)pixWide;
pSize.height = (float)pixHigh;
if (aspectRatio < 1.0) pSize.width *= 1.0 / aspectRatio; else
if (aspectRatio > 1.0) pSize.height *= aspectRatio;
/* init custom image rep */
[self setSize:&pSize];
[self setPixelsWide:pixWide];
[self setPixelsHigh:pixHigh];
[self setBitsPerSample:8];
[self setAlpha:NO];
[self setNumColors:256];
return self;
}
// -------------------------------------------------------------------------------------
// public advertised methods
// -------------------------------------------------------------------------------------
/* return true if file is gif image (only check file extension) */
+ (BOOL)validImageFile:(const char*)filename
{
char *extn = rindex((char*)filename, '.');
return (extn && (!strcmp(extn, ".gif") || !strcmp(extn, ".GIF")))? YES : NO;
}
/* check file type */
+ (BOOL)validImageType:(FILE*)fd
{
char buf[16];
fread(buf, 1, 6, fd);
return (strncmp(buf, "GIF87a", 6) && strncmp(buf, "GIF89a", 6))? NO : YES;
}
/* read all images into a list */
+ (List*)newListFromFile:(const char*)filename
{
id list, _class = self;
long int pos;
/* verify file */
if (![self validImageFile:filename]) return (id)nil;
/* start loading images */
list = [[[List alloc] initCount:1] empty];
for (pos = 0L;;) {
if (!(self = [[_class alloc] _initFromFile:filename atPos:pos])) break;
[list addObject:self];
pos = imageNextPos;
}
/* return list */
if (![list count]) { [list free]; list = (id)nil; }
return list;
}
/* init from file stream */
- initFromFile:(const char*)filename
{
if (![[self class] validImageFile:filename]) { [self free]; return (id)nil; }
return [self _initFromFile:filename atPos:0L];
}
/* free gif */
- free
{
if (imageRaster) free(imageRaster);
if (imageStream) fclose(imageStream);
return [super free];
}
// -------------------------------------------------------------------------------------
// custom image draw
// -------------------------------------------------------------------------------------
/* draw image */
- _drawImage:mySelf
{
int r, c, bpr;
u_char *p;
NXRect rect = { { 0.0, 0.0 }, { 0.0, 0.0 } };
const u_char *bitmap[5] = { 0, 0, 0, 0, 0 };
/* make sure the image raster is loaded */
if (loadError || (!imageRaster && (loadError=[self _loadImage]))) return self;
/* build image bitmap */
bitmap[0] = p = (u_char*)malloc(3L * rasterSize);
for (r = rasterTop; r < rasterTop + pixHigh; r++) {
u_char *s = &imageRaster[r * pixWide];
for (c = 0; c < pixWide; c++, p+=3) memcpy(p, colorMap[s[c]], 3);
}
/* render bitmap */
rect.size = size;
bpr = (7 + pixWide * 8 * 3) / 8;
NXDrawBitmap(&rect, pixWide, pixHigh, 8, 3, 8*3, bpr, NO, NO, NX_RGBColorSpace, bitmap);
/* free bitmap storage */
free((char*)bitmap[0]);
/* return successful */
return self;
}
@end